#include "wx/wxprec.h"
#include "wx/generic/dirctrlg.h"
#include "wx/filefn.h"
#include "wx/imaglist.h"
#include "wx/tokenzr.h"
#include "wx/dir.h"
#include "wx/artprov.h"
#include "wx/mimetype.h"
#include "wx/statline.h"

#ifdef GetFirstChild
#undef GetFirstChild
#endif

size_t wxGetAvailableDrives( wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids ) {
  paths.Add( wxT( "/" ) );
  names.Add( wxT( "/" ) );
  icon_ids.Add( wxFileIconsTable::computer );
  wxASSERT_MSG( ( paths.GetCount() == names.GetCount() ), wxT( "路径的数目及其可读名称的数目应相等." ) );
  wxASSERT_MSG( ( paths.GetCount() == icon_ids.GetCount() ), wxT( "可用驱动器的图标数目错误." ) );
  return paths.GetCount();
}

static int wxCMPFUNC_CONV wxDirCtrlStringCompareFunction( const wxString& strFirst, const wxString& strSecond ) {
  return strFirst.CmpNoCase( strSecond );
}

wxDirItemData::wxDirItemData( const wxString& path, const wxString& name, bool isDir ) {
  m_path = path;
  m_name = name;
  m_isHidden = false;
  m_isExpanded = false;
  m_isDir = isDir;
}

void wxDirItemData::SetNewDirName( const wxString& path ) {
  m_path = path;
  m_name = wxFileNameFromPath( path );
}

bool wxDirItemData::HasSubDirs() const {
  if( m_path.empty() ) {
    return false;
  }
  wxDir dir;
  wxLogNull nolog;
  if( !dir.Open( m_path ) ) {
    return false;
  }
  return dir.HasSubDirs();
}

bool wxDirItemData::HasFiles( const wxString& ( spec ) ) const {
  if( m_path.empty() ) {
    return false;
  }
  wxDir dir;
  wxLogNull nolog;
  if( !dir.Open( m_path ) ) {
    return false;
  }
  return dir.HasFiles();
}

IMPLEMENT_DYNAMIC_CLASS( wxGenericDirCtrl, wxControl )

BEGIN_EVENT_TABLE( wxGenericDirCtrl, wxControl )
  EVT_TREE_ITEM_EXPANDING( wxID_TREECTRL, wxGenericDirCtrl::OnExpandItem )
  EVT_TREE_ITEM_COLLAPSED( wxID_TREECTRL, wxGenericDirCtrl::OnCollapseItem )
  EVT_TREE_BEGIN_LABEL_EDIT( wxID_TREECTRL, wxGenericDirCtrl::OnBeginEditItem )
  EVT_TREE_END_LABEL_EDIT( wxID_TREECTRL, wxGenericDirCtrl::OnEndEditItem )
  EVT_SIZE( wxGenericDirCtrl::OnSize )
END_EVENT_TABLE()

wxGenericDirCtrl::wxGenericDirCtrl( void ) {
  Init();
}

void wxGenericDirCtrl::ExpandRoot() {
  ExpandDir( m_rootId );
  if( !m_defaultPath.empty() ) {
    ExpandPath( m_defaultPath );
  }  else {
    ExpandPath( wxT( "/" ) );
  }
}

bool wxGenericDirCtrl::Create( wxWindow *parent, const wxWindowID id, const wxString& dir, const wxPoint& pos, const wxSize& size,
                               long style, const wxString& filter, int defaultFilter, const wxString& name ) {
  if( !wxControl::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) {
    return false;
  }
  SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE ) );
  SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
  Init();
  long treeStyle = wxTR_HAS_BUTTONS;
  treeStyle |= wxTR_HIDE_ROOT;
  treeStyle |= wxTR_NO_LINES;
  if( style & wxDIRCTRL_EDIT_LABELS ) {
    treeStyle |= wxTR_EDIT_LABELS;
  }
  if( ( style & wxDIRCTRL_3D_INTERNAL ) == 0 ) {
    treeStyle |= wxNO_BORDER;
  } else {
    treeStyle |= wxBORDER_SUNKEN;
  }
  long filterStyle = 0;
  if( ( style & wxDIRCTRL_3D_INTERNAL ) == 0 ) {
    filterStyle |= wxNO_BORDER;
  } else {
    filterStyle |= wxBORDER_SUNKEN;
  }
  m_treeCtrl = CreateTreeCtrl( this, wxID_TREECTRL, wxPoint( 0, 0 ), GetClientSize(), treeStyle );
  if( !filter.empty() && ( style & wxDIRCTRL_SHOW_FILTERS ) ) {
    m_filterListCtrl = new wxDirFilterListCtrl( this, wxID_FILTERLISTCTRL, wxDefaultPosition, wxDefaultSize, filterStyle );
  }
  m_defaultPath = dir;
  m_filter = filter;
  if( m_filter.empty() ) {
    m_filter = wxT( "*" );
  }
  SetFilterIndex( defaultFilter );
  if( m_filterListCtrl ) {
    m_filterListCtrl->FillFilterList( filter, defaultFilter );
  }
  m_treeCtrl->SetImageList( wxTheFileIconsTable->GetSmallImageList() );
  m_showHidden = false;
  wxDirItemData* rootData = new wxDirItemData( wxEmptyString, wxEmptyString, true );
  wxString rootName;
  rootName = _( "Sections" );
  m_rootId = m_treeCtrl->AddRoot( rootName, 3, -1, rootData );
  m_treeCtrl->SetItemHasChildren( m_rootId );
  ExpandRoot();
  SetInitialSize( size );
  DoResize();
  return true;
}

wxGenericDirCtrl::~wxGenericDirCtrl() {
}

void wxGenericDirCtrl::Init() {
  m_showHidden = false;
  m_currentFilter = 0;
  m_currentFilterStr = wxEmptyString; // Default: any file
  m_treeCtrl = NULL;
  m_filterListCtrl = NULL;
}

wxTreeCtrl* wxGenericDirCtrl::CreateTreeCtrl( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long treeStyle ) {
  return new wxTreeCtrl( parent, id, pos, size, treeStyle );
}

void wxGenericDirCtrl::ShowHidden( bool show ) {
  m_showHidden = show;
  wxString path = GetPath();
  ReCreateTree();
  SetPath( path );
}

const wxTreeItemId
wxGenericDirCtrl::AddSection( const wxString& path, const wxString& name, int imageId ) {
  wxDirItemData *dir_item = new wxDirItemData( path, name, true );
  wxTreeItemId id = AppendItem( m_rootId, name, imageId, -1, dir_item );
  m_treeCtrl->SetItemHasChildren( id );
  return id;
}

void wxGenericDirCtrl::SetupSections() {
  wxArrayString paths, names;
  wxArrayInt icons;
  size_t n, count = wxGetAvailableDrives( paths, names, icons );
  wxString home = wxGetHomeDir();
  AddSection( home, _( "Home directory" ), 1 );
  home += wxT( "/Desktop" );
  AddSection( home, _( "Desktop" ), 1 );
  for( n = 0; n < count; n++ ) {
    AddSection( paths[n], names[n], icons[n] );
  }
}

void wxGenericDirCtrl::OnBeginEditItem( wxTreeEvent &event ) {
  if( event.GetItem() == m_rootId ) {
    event.Veto();
    return;
  }
  if( m_treeCtrl->GetItemParent( event.GetItem() ) == m_rootId ) {
    event.Veto();
    return;
  }
}

void wxGenericDirCtrl::OnEndEditItem( wxTreeEvent &event ) {
  if( event.IsEditCancelled() ) {
    return;
  }
  if( ( event.GetLabel().empty() ) ||
      ( event.GetLabel() == wxT( "." ) ) ||
      ( event.GetLabel() == wxT( ".." ) ) ||
      ( event.GetLabel().Find( wxT( '/' ) ) != wxNOT_FOUND ) ||
      ( event.GetLabel().Find( wxT( '\\' ) ) != wxNOT_FOUND ) ||
      ( event.GetLabel().Find( wxT( '|' ) ) != wxNOT_FOUND ) ) {
    wxMessageDialog dialog( this, _( "Illegal directory name." ), _( "错误" ), wxOK | wxICON_ERROR );
    dialog.ShowModal();
    event.Veto();
    return;
  }
  wxTreeItemId id = event.GetItem();
  wxDirItemData *data = ( wxDirItemData* )m_treeCtrl->GetItemData( id );
  wxASSERT( data );
  wxString new_name( wxPathOnly( data->m_path ) );
  new_name += wxString( wxFILE_SEP_PATH );
  new_name += event.GetLabel();
  wxLogNull log;
  if( wxFileExists( new_name ) ) {
    wxMessageDialog dialog( this, _( "File name exists already." ), _( "错误" ), wxOK | wxICON_ERROR );
    dialog.ShowModal();
    event.Veto();
  }
  if( wxRenameFile( data->m_path, new_name ) ) {
    data->SetNewDirName( new_name );
  } else {
    wxMessageDialog dialog( this, _( "Operation not permitted." ), _( "错误" ), wxOK | wxICON_ERROR );
    dialog.ShowModal();
    event.Veto();
  }
}

void wxGenericDirCtrl::OnExpandItem( wxTreeEvent &event ) {
  wxTreeItemId parentId = event.GetItem();
  if( !m_rootId.IsOk() ) {
    m_rootId = m_treeCtrl->GetRootItem();
  }
  ExpandDir( parentId );
}

void wxGenericDirCtrl::OnCollapseItem( wxTreeEvent &event ) {
  CollapseDir( event.GetItem() );
}

void wxGenericDirCtrl::CollapseDir( wxTreeItemId parentId ) {
  wxTreeItemId child;
  wxDirItemData *data = ( wxDirItemData * ) m_treeCtrl->GetItemData( parentId );
  if( !data->m_isExpanded ) {
    return;
  }
  data->m_isExpanded = false;
  m_treeCtrl->Freeze();
  if( parentId != m_treeCtrl->GetRootItem() ) {
    m_treeCtrl->CollapseAndReset( parentId );
  }
  m_treeCtrl->DeleteChildren( parentId );
  m_treeCtrl->Thaw();
}

void wxGenericDirCtrl::ExpandDir( wxTreeItemId parentId ) {
  wxDirItemData *data = ( wxDirItemData * ) m_treeCtrl->GetItemData( parentId );
  if( data->m_isExpanded ) {
    return;
  }
  data->m_isExpanded = true;
  if( parentId == m_treeCtrl->GetRootItem() ) {
    SetupSections();
    return;
  }
  wxASSERT( data );
  wxString search, path, filename;
  wxString dirName( data->m_path );
  wxBusyCursor busy;
  wxArrayString dirs;
  wxArrayString filenames;
  wxDir d;
  wxString eachFilename;
  wxLogNull log;
  d.Open( dirName );
  if( d.IsOpened() ) {
    int style = wxDIR_DIRS;
    if( m_showHidden ) {
      style |= wxDIR_HIDDEN;
    }
    if( d.GetFirst( & eachFilename, wxEmptyString, style ) ) {
      do {
        if( ( eachFilename != wxT( "." ) ) && ( eachFilename != wxT( ".." ) ) ) {
          dirs.Add( eachFilename );
        }
      } while( d.GetNext( &eachFilename ) );
    }
  }
  dirs.Sort( wxDirCtrlStringCompareFunction );
  if( ( GetWindowStyle() & wxDIRCTRL_DIR_ONLY ) == 0 ) {
    d.Open( dirName );
    if( d.IsOpened() ) {
      int style = wxDIR_FILES;
      if( m_showHidden ) {
        style |= wxDIR_HIDDEN;
      }
      wxStringTokenizer strTok;
      wxString curFilter;
      strTok.SetString( m_currentFilterStr, wxT( ";" ) );
      while( strTok.HasMoreTokens() ) {
        curFilter = strTok.GetNextToken();
        if( d.GetFirst( & eachFilename, curFilter, style ) ) {
          do {
            if( ( eachFilename != wxT( "." ) ) && ( eachFilename != wxT( ".." ) ) )
            { filenames.Add( eachFilename ); }
          } while( d.GetNext( & eachFilename ) );
        }
      }
    }
    filenames.Sort( wxDirCtrlStringCompareFunction );
  }
  // Add the sorted dirs
  size_t i;
  for( i = 0; i < dirs.Count(); i++ ) {
    eachFilename = dirs[i];
    path = dirName;
    if( !wxEndsWithPathSeparator( path ) ) {
      path += wxString( wxFILE_SEP_PATH );
    }
    path += eachFilename;
    wxDirItemData *dir_item = new wxDirItemData( path, eachFilename, true );
    wxTreeItemId id = AppendItem( parentId, eachFilename, wxFileIconsTable::folder, -1, dir_item );
    m_treeCtrl->SetItemImage( id, wxFileIconsTable::folder_open, wxTreeItemIcon_Expanded );
    if( dir_item->HasSubDirs() ||
        ( ( ( GetWindowStyle() & wxDIRCTRL_DIR_ONLY ) == 0 ) &&
          dir_item->HasFiles( m_currentFilterStr ) ) ) {
      m_treeCtrl->SetItemHasChildren( id );
    }
  }
  if( ( GetWindowStyle() & wxDIRCTRL_DIR_ONLY ) == 0 ) {
    for( i = 0; i < filenames.Count(); i++ ) {
      eachFilename = filenames[i];
      path = dirName;
      if( !wxEndsWithPathSeparator( path ) ) {
        path += wxString( wxFILE_SEP_PATH );
      }
      path += eachFilename;
      wxDirItemData *dir_item = new wxDirItemData( path, eachFilename, false );
      int image_id = wxFileIconsTable::file;
      if( eachFilename.Find( wxT( '.' ) ) != wxNOT_FOUND ) {
        image_id = wxTheFileIconsTable->GetIconID( eachFilename.AfterLast( wxT( '.' ) ) );
      }
      ( void ) AppendItem( parentId, eachFilename, image_id, -1, dir_item );
    }
  }
}

void wxGenericDirCtrl::ReCreateTree() {
  CollapseDir( m_treeCtrl->GetRootItem() );
  ExpandRoot();
}

void wxGenericDirCtrl::CollapseTree() {
  wxTreeItemIdValue cookie;
  wxTreeItemId child = m_treeCtrl->GetFirstChild( m_rootId, cookie );
  while( child.IsOk() ) {
    CollapseDir( child );
    child = m_treeCtrl->GetNextChild( m_rootId, cookie );
  }
}

wxTreeItemId wxGenericDirCtrl::FindChild( wxTreeItemId parentId, const wxString& path, bool& done ) {
  wxString path2( path );
  path2.Replace( wxT( "\\" ), wxString( wxFILE_SEP_PATH ) );
  path2.Replace( wxT( "/" ), wxString( wxFILE_SEP_PATH ) );
  path2 += wxString( wxFILE_SEP_PATH );
  wxTreeItemIdValue cookie;
  wxTreeItemId childId = m_treeCtrl->GetFirstChild( parentId, cookie );
  while( childId.IsOk() ) {
    wxDirItemData* data = ( wxDirItemData* ) m_treeCtrl->GetItemData( childId );
    if( data && !data->m_path.empty() ) {
      wxString childPath( data->m_path );
      if( !wxEndsWithPathSeparator( childPath ) ) {
        childPath += wxString( wxFILE_SEP_PATH );
      }
      if( childPath.length() <= path2.length() ) {
        wxString path3 = path2.Mid( 0, childPath.length() );
        if( childPath == path3 ) {
          if( path3.length() == path2.length() ) {
            done = true;
          } else {
            done = false;
          }
          return childId;
        }
      }
    }
    childId = m_treeCtrl->GetNextChild( parentId, cookie );
  }
  wxTreeItemId invalid;
  return invalid;
}

bool wxGenericDirCtrl::ExpandPath( const wxString& path ) {
  bool done = false;
  wxTreeItemId id = FindChild( m_rootId, path, done );
  wxTreeItemId lastId = id; // The last non-zero id
  while( id.IsOk() && !done ) {
    ExpandDir( id );
    id = FindChild( id, path, done );
    if( id.IsOk() ) {
      lastId = id;
    }
  }
  if( !lastId.IsOk() ) {
    return false;
  }
  wxDirItemData *data = ( wxDirItemData * ) m_treeCtrl->GetItemData( lastId );
  if( data->m_isDir ) {
    m_treeCtrl->Expand( lastId );
  }
  if( ( GetWindowStyle() & wxDIRCTRL_SELECT_FIRST ) && data->m_isDir ) {
    wxTreeItemIdValue cookie;
    wxTreeItemId childId = m_treeCtrl->GetFirstChild( lastId, cookie );
    bool selectedChild = false;
    while( childId.IsOk() ) {
      data = ( wxDirItemData* ) m_treeCtrl->GetItemData( childId );
      if( data && data->m_path != wxEmptyString && !data->m_isDir ) {
        m_treeCtrl->SelectItem( childId );
        m_treeCtrl->EnsureVisible( childId );
        selectedChild = true;
        break;
      }
      childId = m_treeCtrl->GetNextChild( lastId, cookie );
    }
    if( !selectedChild ) {
      m_treeCtrl->SelectItem( lastId );
      m_treeCtrl->EnsureVisible( lastId );
    }
  } else {
    m_treeCtrl->SelectItem( lastId );
    m_treeCtrl->EnsureVisible( lastId );
  }
  return true;
}


bool wxGenericDirCtrl::CollapsePath( const wxString& path ) {
  bool done = false;
  wxTreeItemId id = FindChild( m_rootId, path, done );
  wxTreeItemId lastId = id;
  while( id.IsOk() && !done ) {
    CollapseDir( id );
    id = FindChild( id, path, done );
    if( id.IsOk() ) {
      lastId = id;
    }
  }
  if( !lastId.IsOk() ) {
    return false;
  }
  m_treeCtrl->SelectItem( lastId );
  m_treeCtrl->EnsureVisible( lastId );
  return true;
}

wxString wxGenericDirCtrl::GetPath() const {
  wxTreeItemId id = m_treeCtrl->GetSelection();
  if( id ) {
    wxDirItemData* data = ( wxDirItemData* ) m_treeCtrl->GetItemData( id );
    return data->m_path;
  } else {
    return wxEmptyString;
  }
}

wxString wxGenericDirCtrl::GetFilePath() const {
  wxTreeItemId id = m_treeCtrl->GetSelection();
  if( id ) {
    wxDirItemData* data = ( wxDirItemData* ) m_treeCtrl->GetItemData( id );
    if( data->m_isDir ) {
      return wxEmptyString;
    } else {
      return data->m_path;
    }
  } else {
    return wxEmptyString;
  }
}

void wxGenericDirCtrl::SetPath( const wxString& path ) {
  m_defaultPath = path;
  if( m_rootId ) {
    ExpandPath( path );
  }
}

void wxGenericDirCtrl::SetFilterIndex( int n ) {
  m_currentFilter = n;
  wxString f, d;
  if( ExtractWildcard( m_filter, n, f, d ) ) {
    m_currentFilterStr = f;
  } else {
    m_currentFilterStr = wxT( "*" );
  }
}

void wxGenericDirCtrl::SetFilter( const wxString& filter ) {
  m_filter = filter;
  wxString f, d;
  if( ExtractWildcard( m_filter, m_currentFilter, f, d ) ) {
    m_currentFilterStr = f;
  } else {
    m_currentFilterStr = wxT( "*" );
  }
  SetFilterIndex( 0 );
  if( m_filterListCtrl ) {
    m_filterListCtrl->FillFilterList( m_filter, 0 );
  }
}

bool wxGenericDirCtrl::ExtractWildcard( const wxString& filterStr, int n, wxString& filter, wxString& description ) {
  wxArrayString filters, descriptions;
  int count = wxParseCommonDialogsFilter( filterStr, descriptions, filters );
  if( count > 0 && n < count ) {
    filter = filters[n];
    description = descriptions[n];
    return true;
  }
  return false;
}

void wxGenericDirCtrl::DoResize() {
  wxSize sz = GetClientSize();
  int verticalSpacing = 3;
  if( m_treeCtrl ) {
    wxSize filterSz ;
    if( m_filterListCtrl ) {
      filterSz = m_filterListCtrl->GetSize();
      sz.y -= ( filterSz.y + verticalSpacing );
    }
    m_treeCtrl->SetSize( 0, 0, sz.x, sz.y );
    if( m_filterListCtrl ) {
      m_filterListCtrl->SetSize( 0, sz.y + verticalSpacing, sz.x, filterSz.y );
      m_filterListCtrl->Refresh();
    }
  }
}

void wxGenericDirCtrl::OnSize( wxSizeEvent& ( event ) ) {
  DoResize();
}

wxTreeItemId wxGenericDirCtrl::AppendItem( const wxTreeItemId & parent,
    const wxString & text,
    int image, int selectedImage,
    wxTreeItemData * data ) {
  wxTreeCtrl *treeCtrl = GetTreeCtrl();
  wxASSERT( treeCtrl );
  if( treeCtrl ) {
    return treeCtrl->AppendItem( parent, text, image, selectedImage, data );
  } else {
    return wxTreeItemId();
  }
}

IMPLEMENT_CLASS( wxDirFilterListCtrl, wxChoice )

BEGIN_EVENT_TABLE( wxDirFilterListCtrl, wxChoice )
  EVT_CHOICE( wxID_ANY, wxDirFilterListCtrl::OnSelFilter )
END_EVENT_TABLE()

bool wxDirFilterListCtrl::Create( wxGenericDirCtrl* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) {
  m_dirCtrl = parent;
  return wxChoice::Create( parent, id, pos, size, 0, NULL, style );
}

void wxDirFilterListCtrl::Init() {
  m_dirCtrl = NULL;
}

void wxDirFilterListCtrl::OnSelFilter( wxCommandEvent& ( event ) ) {
  int sel = GetSelection();
  wxString currentPath = m_dirCtrl->GetPath();
  m_dirCtrl->SetFilterIndex( sel );
  // If the filter has changed, the view is out of date, so
  // collapse the tree.
  m_dirCtrl->ReCreateTree();
  // Try to restore the selection, or at least the directory
  m_dirCtrl->ExpandPath( currentPath );
}

void wxDirFilterListCtrl::FillFilterList( const wxString& filter, int defaultFilter ) {
  Clear();
  wxArrayString descriptions, filters;
  size_t n = ( size_t ) wxParseCommonDialogsFilter( filter, descriptions, filters );
  if( n > 0 && defaultFilter < ( int ) n ) {
    for( size_t i = 0; i < n; i++ ) {
      Append( descriptions[i] );
    }
    SetSelection( defaultFilter );
  }
}

wxFileIconsTable* wxTheFileIconsTable = ( wxFileIconsTable * )NULL;

class wxFileIconsTableModule: public wxModule {
    DECLARE_DYNAMIC_CLASS( wxFileIconsTableModule )
  public:
    wxFileIconsTableModule() {}
    bool OnInit() { wxTheFileIconsTable = new wxFileIconsTable; return true; }
    void OnExit() {
      if( wxTheFileIconsTable ) {
        delete wxTheFileIconsTable;
        wxTheFileIconsTable = NULL;
      }
    }
};

IMPLEMENT_DYNAMIC_CLASS( wxFileIconsTableModule, wxModule )

class wxFileIconEntry : public wxObject {
  public:
    wxFileIconEntry( int i ) { id = i; }

    int id;
};

wxFileIconsTable::wxFileIconsTable() {
  m_HashTable = NULL;
  m_smallImageList = NULL;
}

wxFileIconsTable::~wxFileIconsTable() {
  if( m_HashTable ) {
    WX_CLEAR_HASH_TABLE( *m_HashTable );
    delete m_HashTable;
  }
  if( m_smallImageList ) {
    delete m_smallImageList;
  }
}

void wxFileIconsTable::Create() {
  wxCHECK_RET( !m_smallImageList && !m_HashTable, wxT( "creating icons twice" ) );
  m_HashTable = new wxHashTable( wxKEY_STRING );
  m_smallImageList = new wxImageList( 16, 16 );
  // folder:
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_FOLDER,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // folder_open
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_FOLDER_OPEN,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // computer
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_HARDDISK,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_HARDDISK,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // cdrom
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_CDROM,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // floppy
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_FLOPPY,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // removeable
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_REMOVABLE,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // file
  m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_NORMAL_FILE,
                         wxART_CMN_DIALOG,
                         wxSize( 16, 16 ) ) );
  // executable
  if( GetIconID( wxEmptyString, _T( "application/x-executable" ) ) == file ) {
    m_smallImageList->Add( wxArtProvider::GetBitmap( wxART_EXECUTABLE_FILE,
                           wxART_CMN_DIALOG,
                           wxSize( 16, 16 ) ) );
    delete m_HashTable->Get( _T( "exe" ) );
    m_HashTable->Delete( _T( "exe" ) );
    m_HashTable->Put( _T( "exe" ), new wxFileIconEntry( executable ) );
  }
}

wxImageList *wxFileIconsTable::GetSmallImageList() {
  if( !m_smallImageList ) {
    Create();
  }
  return m_smallImageList;
}

static wxBitmap CreateAntialiasedBitmap( const wxImage& img ) {
  const unsigned int size = 16;
  wxImage smallimg( size, size );
  unsigned char *p1, *p2, *ps;
  unsigned char mr = img.GetMaskRed(),
                mg = img.GetMaskGreen(),
                mb = img.GetMaskBlue();
  unsigned x, y;
  unsigned sr, sg, sb, smask;
  p1 = img.GetData(), p2 = img.GetData() + 3 * size * 2, ps = smallimg.GetData();
  smallimg.SetMaskColour( mr, mr, mr );
  for( y = 0; y < size; y++ ) {
    for( x = 0; x < size; x++ ) {
      sr = sg = sb = smask = 0;
      if( p1[0] != mr || p1[1] != mg || p1[2] != mb ) {
        sr += p1[0], sg += p1[1], sb += p1[2];
      } else {
        smask++;
      }
      p1 += 3;
      if( p1[0] != mr || p1[1] != mg || p1[2] != mb ) {
        sr += p1[0], sg += p1[1], sb += p1[2];
      } else {
        smask++;
      }
      p1 += 3;
      if( p2[0] != mr || p2[1] != mg || p2[2] != mb ) {
        sr += p2[0], sg += p2[1], sb += p2[2];
      } else {
        smask++;
      }
      p2 += 3;
      if( p2[0] != mr || p2[1] != mg || p2[2] != mb ) {
        sr += p2[0], sg += p2[1], sb += p2[2];
      } else {
        smask++;
      }
      p2 += 3;
      if( smask > 2 ) {
        ps[0] = ps[1] = ps[2] = mr;
      } else {
        ps[0] = ( unsigned char )( sr >> 2 );
        ps[1] = ( unsigned char )( sg >> 2 );
        ps[2] = ( unsigned char )( sb >> 2 );
      }
      ps += 3;
    }
    p1 += size * 2 * 3, p2 += size * 2 * 3;
  }
  return wxBitmap( smallimg );
}

int wxFileIconsTable::GetIconID( const wxString& extension, const wxString& mime ) {
  if( !m_smallImageList ) {
    Create();
  }
  #if wxUSE_MIMETYPE
  if( !extension.empty() ) {
    wxFileIconEntry *entry = ( wxFileIconEntry* ) m_HashTable->Get( extension );
    if( entry ) {
      return ( entry -> id );
    }
  }
  wxFileType *ft = ( mime.empty() ) ?
                   wxTheMimeTypesManager -> GetFileTypeFromExtension( extension ) :
                   wxTheMimeTypesManager -> GetFileTypeFromMimeType( mime );
  wxIconLocation iconLoc;
  wxIcon ic;
  {
    wxLogNull logNull;
    if( ft && ft->GetIcon( &iconLoc ) ) {
      ic = wxIcon( iconLoc );
    }
  }
  delete ft;
  if( !ic.Ok() ) {
    int newid = file;
    m_HashTable->Put( extension, new wxFileIconEntry( newid ) );
    return newid;
  }
  wxBitmap bmp;
  bmp.CopyFromIcon( ic );
  if( !bmp.Ok() ) {
    int newid = file;
    m_HashTable->Put( extension, new wxFileIconEntry( newid ) );
    return newid;
  }
  const unsigned int size = 16;
  int id = m_smallImageList->GetImageCount();
  if( ( bmp.GetWidth() == ( int ) size ) && ( bmp.GetHeight() == ( int ) size ) ) {
    m_smallImageList->Add( bmp );
  }
  #if wxUSE_IMAGE && (!defined(__WXMSW__) || wxUSE_WXDIB)
  else {
    wxImage img = bmp.ConvertToImage();
    if( ( img.GetWidth() != size * 2 ) || ( img.GetHeight() != size * 2 ) )
      //            m_smallImageList->Add(CreateAntialiasedBitmap(CutEmptyBorders(img).Rescale(size*2, size*2)));
    { m_smallImageList->Add( CreateAntialiasedBitmap( img.Rescale( size * 2, size * 2 ) ) ); }
    else
    { m_smallImageList->Add( CreateAntialiasedBitmap( img ) ); }
  }
  #endif // wxUSE_IMAGE
  m_HashTable->Put( extension, new wxFileIconEntry( id ) );
  return id;
  #else // !wxUSE_MIMETYPE
  wxUnusedVar( mime );
  if( extension == wxT( "exe" ) ) {
    return executable;
  } else
  { return file; }
  #endif // wxUSE_MIMETYPE/!wxUSE_MIMETYPE
}
